using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Dyn.News.Models;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.ContentManagement;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.Tasks.Scheduling;

namespace Dyn.News.Services {
    [UsedImplicitly]
    public class ArticleService : IArticleService {
        private readonly IContentManager _contentManager;
        private readonly IRepository<CategoryPartArchiveRecord> _categoryArchiveRepository;
        private readonly IPublishingTaskManager _publishingTaskManager;

        public ArticleService(IContentManager contentManager, IRepository<CategoryPartArchiveRecord> categoryArchiveRepository, IPublishingTaskManager publishingTaskManager) {
            _contentManager = contentManager;
            _categoryArchiveRepository = categoryArchiveRepository;
            _publishingTaskManager = publishingTaskManager;
        }

        public ArticlePart Get(NewsCategoryPart categoryPart, string slug) {
            return Get(categoryPart, slug, VersionOptions.Published);
        }

        public ArticlePart Get(NewsCategoryPart categoryPart, string slug, VersionOptions versionOptions) {
            var categoryPath = categoryPart.As<IRoutableAspect>().GetChildPath(slug);
            return
                _contentManager.Query(versionOptions, "Article").Join<RoutePartRecord>().Where(rr => rr.Path == categoryPath).
                    Join<CommonPartRecord>().Where(cr => cr.Container == categoryPart.Record.ContentItemRecord).List().
                    SingleOrDefault().As<ArticlePart>();
        }

        public ArticlePart Get(int id) {
            return Get(id, VersionOptions.Published);
        }

        public ArticlePart Get(int id, VersionOptions versionOptions) {
            return _contentManager.Get<ArticlePart>(id, versionOptions);
        }

        public IEnumerable<ArticlePart> Get(NewsCategoryPart CategoryPart) {
            return Get(CategoryPart, VersionOptions.Published);
        }

        public IEnumerable<ArticlePart> Get(NewsCategoryPart CategoryPart, VersionOptions versionOptions) {
            return GetCategoryQuery(CategoryPart, versionOptions).List().Select(ci => ci.As<ArticlePart>());
        }

        public IEnumerable<ArticlePart> Get(NewsCategoryPart CategoryPart, int skip, int count) {
            return Get(CategoryPart, skip, count, VersionOptions.Published);
        }

        public IEnumerable<ArticlePart> Get(NewsCategoryPart CategoryPart, int skip, int count, VersionOptions versionOptions) {
            return GetCategoryQuery(CategoryPart, versionOptions).Slice(skip, count).ToList().Select(ci => ci.As<ArticlePart>());
        }

        public int PostCount(NewsCategoryPart CategoryPart) {
            return PostCount(CategoryPart, VersionOptions.Published);
        }

        public int PostCount(NewsCategoryPart CategoryPart, VersionOptions versionOptions) {
            return GetCategoryQuery(CategoryPart, versionOptions).Count();
        }

        public IEnumerable<ArticlePart> Get(NewsCategoryPart CategoryPart, ArchiveData archiveData) {
            var query = GetCategoryQuery(CategoryPart, VersionOptions.Published);

            if (archiveData.Day > 0) {
                var dayDate = new DateTime(archiveData.Year, archiveData.Month, archiveData.Day);

                query = query.Where(cr => cr.CreatedUtc >= dayDate && cr.CreatedUtc < dayDate.AddDays(1));
            }
            else if (archiveData.Month > 0)
            {
                var monthDate = new DateTime(archiveData.Year, archiveData.Month, 1);

                query = query.Where(cr => cr.CreatedUtc >= monthDate && cr.CreatedUtc < monthDate.AddMonths(1));
            }
            else {
                var yearDate = new DateTime(archiveData.Year, 1, 1);

                query = query.Where(cr => cr.CreatedUtc >= yearDate && cr.CreatedUtc < yearDate.AddYears(1));
            }

            return query.List().Select(ci => ci.As<ArticlePart>());
        }

        public IEnumerable<KeyValuePair<ArchiveData, int>> GetArchives(NewsCategoryPart CategoryPart) {
            var query = 
                from bar in _categoryArchiveRepository.Table
                where bar.CategoryPart== CategoryPart.Record
                orderby bar.Year descending, bar.Month descending
                select bar;

            return
                query.ToList().Select(
                    bar =>
                    new KeyValuePair<ArchiveData, int>(new ArchiveData(string.Format("{0}/{1}", bar.Year, bar.Month)),
                                                       bar.PostCount));
        }

        public void Delete(ArticlePart ArticlePart) {
            _publishingTaskManager.DeleteTasks(ArticlePart.ContentItem);
            _contentManager.Remove(ArticlePart.ContentItem);
        }

        public void Publish(ArticlePart ArticlePart) {
            _publishingTaskManager.DeleteTasks(ArticlePart.ContentItem);
            _contentManager.Publish(ArticlePart.ContentItem);
        }

        public void Publish(ArticlePart ArticlePart, DateTime scheduledPublishUtc) {
            _publishingTaskManager.Publish(ArticlePart.ContentItem, scheduledPublishUtc);
        }

        public void Unpublish(ArticlePart ArticlePart) {
            _contentManager.Unpublish(ArticlePart.ContentItem);
        }

        public DateTime? GetScheduledPublishUtc(ArticlePart ArticlePart) {
            var task = _publishingTaskManager.GetPublishTask(ArticlePart.ContentItem);
            return (task == null ? null : task.ScheduledUtc);
        }

        private IContentQuery<ContentItem, CommonPartRecord> GetCategoryQuery(ContentPart<NewsCategoryPartRecord> Category, VersionOptions versionOptions) {
            return
                _contentManager.Query(versionOptions, "Article").Join<CommonPartRecord>().Where(
                    cr => cr.Container == Category.Record.ContentItemRecord).OrderByDescending(cr => cr.CreatedUtc);
        }
    }
}